<template>
  <div ref="dsTableWrap" class="relative flex flex-col h-full items-center justify-center overflow-hidden">
    <ds-spin v-if="dsLoading"/>
    <div v-else class="ds-table absolute inset-0 box-border text-primary w-full overflow-hidden">
      <div ref="dsTableHead" class="table table-fixed border-collapse w-full relative z-10 h-10">
        <div class="table-row border-b-2 border-opacity-20 border-primary">
          <div class="table-cell align-middle px-2 py-2" :class="col.width" v-for="col in dsTable.column">{{ col.label }}</div>
        </div>
      </div>
      <div ref="dsTableBody" class="ds-table-body overflow-hidden">
        <div ref="dsTableContent" class="table table-fixed w-full text-sm">
          <div ref="dsTableRow" class="table-row h-10" v-for="(row, index) in dsTable.data">
            <div class="table-cell align-middle px-2 py-2" :class="dsTable.column[index]['width']" :key="index" v-for="(col, index) in dsTable.column">
              <div class="truncate">{{ row[col.prop] }}</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import {defineComponent, ref, reactive, onMounted, inject, nextTick} from 'vue';
import DEFAULT_OPTION from '../assets/data/defaultOption';
import merge from 'lodash/merge';
import HTTP from '../apis';
import DsLabel from './DsLabel.vue';
import DsSpin from './DsSpin.vue';
import {Emitter} from 'mitt';

interface ColumnVO {
  label: string,
  prop: string
}

export default defineComponent({
  name: 'DsScrollTable',
  components: {DsSpin, DsLabel},
  props: {
    label: {
      type: Object,
      default() {
        return {};
      }
    },
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    column: {
      type: Array,
      default(): ColumnVO[] {
        return [];
      }
    },
    endpoint: {
      type: String,
      default: ''
    },
    scroll: {
      type: Object,
      default() {
        return {
          delay: 3,
          row: 4,
          ease: 'ease-in-out'
        };
      }
    },
    query: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  setup(props) {
    let r: any, init = true, start: number = 0, reqId: number = 0;
    const {scroll, column, data, query} = props;
    const {endpoint} = inject<any>('Config');
    const emitter = inject<Emitter>('Emitter');
    const url = props.endpoint || endpoint;
    const dsLoading = ref<boolean>(true);
    const dsTableWrap = ref<HTMLElement | null>(null);
    const dsTableHead = ref<HTMLElement | null>(null);
    const dsTableBody = ref<HTMLElement | null>(null);
    const dsTableRow = ref<HTMLElement | null>(null);
    const dsTableContent = ref<HTMLElement | null>(null);
    const dsTable = reactive({
      data: [
        ...DEFAULT_OPTION.table.data,
        ...data
      ],
      column: [
        ...DEFAULT_OPTION.table.column,
        ...column
      ]
    });
    const getData = async () => {
      if (init) {
        dsLoading.value = true;
      } else {
        onStop()
      }
      r = await HTTP.get(url, {
        params: query
      });
      if (r && r.data) {
        dsLoading.value = false;
        dsTable.data = r.data.data.data;
        dsTable.column = merge(dsTable.column, r.data.data.column);
        await nextTick(() => {
          onCalc()
          onUpdate()
        })
      }
    };

    const calculate: {
      wrap?: number
      head?: number
      row?: number
      body?: number
      available?: boolean
    } = {};

    const onCalc = () => {
      calculate.wrap = dsTableWrap.value?.getBoundingClientRect().height;
      calculate.head = dsTableHead.value?.getBoundingClientRect().height;
      calculate.row = dsTableRow.value?.getBoundingClientRect().height;
      if (calculate.wrap && calculate.head) {
        calculate.body = calculate.wrap - calculate.head;
        dsTableBody.value!.style.height = calculate.body + 'px';
        if (calculate.row) {
          calculate.available = calculate.body < dsTable.data.length * calculate.row;
        }
      }
    };

    const onInit = () => {
      dsTableBody.value!.style.height = calculate.body + 'px';
      dsTableContent.value!.style.transform = `translate3d(0, 0, 0)`;
      dsTableContent.value?.addEventListener('transitionend', (e) => {
        start = e.timeStamp;
        dsTableContent.value!.style.transition = 'none';
        dsTableContent.value!.style.transform = `translate3d(0, 0, 0)`;
        const pop = dsTable.data.splice(0, scroll.row);
        dsTable.data = dsTable.data.concat(...pop);
      });
    };

    const onStop = () => {
      cancelAnimationFrame(reqId);
      dsTableContent.value?.addEventListener('transitionend', (e) => {
        start = e.timeStamp;
        dsTableContent.value!.style.transition = 'none';
      });
    };


    const onTableScroll = (timestamp: number) => {
      if (!calculate.available) {
        return false;
      }
      if (!start) {
        start = timestamp;
      }
      const elapsed = timestamp - start;

      if (elapsed > scroll.delay * 1000) {
        dsTableContent.value!.style.transition = `transform .3s ${scroll.ease}`;
        dsTableContent.value!.style.transform = `translate3d(0, -${calculate.row * scroll.row}px, 0)`;
      }
      reqId = requestAnimationFrame(onTableScroll);
    };

    const onUpdate = () => {
      reqId = requestAnimationFrame(onTableScroll);
    };

    onMounted(async () => {
      if (url) {
        await getData();
        init = false;
      }
      onCalc();
      onInit();
      onUpdate();
      addEventListener('resize', () => {
        onStop()
        nextTick(() => {
          onCalc()
          onUpdate()
        })
      })
      emitter?.on('update', getData)
    });
    return {
      dsTable,
      dsLoading,
      dsTableWrap,
      dsTableHead,
      dsTableBody,
      dsTableRow,
      dsTableContent,
    };
  },
});
</script>
<style lang="scss">
.ds-table-body {
  .table-row {
    &:nth-child(2n) {
      @apply bg-primary bg-opacity-10
    }
  }
}
</style>
